package com.jeremyliao.android.plugin.transform

import com.android.build.api.transform.*
import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.utils.FileUtils
import com.jeremyliao.android.base.annotation.PrintLog
import javassist.ClassPool
import javassist.CtClass
import javassist.CtMethod
import org.apache.commons.codec.digest.DigestUtils
import org.apache.commons.io.IOUtils
import org.gradle.api.Project
import org.gradle.api.internal.tasks.testing.detection.TestClassVisitor
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter

import java.util.jar.JarEntry
import java.util.jar.JarFile
import java.util.jar.JarOutputStream
import java.util.zip.ZipEntry

import static org.objectweb.asm.ClassReader.EXPAND_FRAMES

/**
 * Created by liaohailiang on 2018/12/26.
 */
class PrintLogTransform extends Transform {

    final String TAG = "[PrintLogTransform]"

    final static ClassPool pool = ClassPool.getDefault()

    final Project project

    PrintLogTransform(Project project) {
        this.project = project
    }

    @Override
    void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        System.out.println(TAG + "start transform")
        super.transform(transformInvocation)
        Collection<TransformInput> inputs = transformInvocation.inputs
        TransformOutputProvider outputProvider = transformInvocation.outputProvider

        //删除之前的输出
        if (outputProvider != null)
            outputProvider.deleteAll()
        //遍历inputs
        inputs.each { TransformInput input ->
            //遍历directoryInputs
            input.directoryInputs.each { DirectoryInput directoryInput ->
                //处理directoryInputs
                handleDirectoryInput(directoryInput, outputProvider)
            }

            //遍历jarInputs
            input.jarInputs.each { JarInput jarInput ->
                //处理jarInputs
                handleJarInputs(jarInput, outputProvider)
            }
        }
    }

    @Override
    String getName() {
        return PrintLogTransform.simpleName
    }

    @Override
    Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

    @Override
    Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT
    }

    @Override
    boolean isIncremental() {
        return false
    }

    /**
     * 处理文件目录下的class文件
     */
    void handleDirectoryInput(DirectoryInput directoryInput, TransformOutputProvider outputProvider) {
        //将当前路径加入类池,不然找不到这个类
        pool.appendClassPath(directoryInput.file.absolutePath)
        pool.appendClassPath(project.android.bootClasspath[0].toString())
        pool.importPackage("android.util.Log")
        String packageName = project.android.defaultConfig.applicationId
        println 'packageName: ' + packageName
        if (directoryInput.file.isDirectory()) {
            directoryInput.file.eachFileRecurse { File file ->
                def name = file.name
                if (name.endsWith(".class") && !name.startsWith("R\$") &&
                        !"R.class".equals(name) && !"BuildConfig.class".equals(name)) {
                    println 'filename: ' + name
                    def filePath = file.absolutePath
                    def filePathWithDot = filePath.replace("\\", ".").replace("/", ".")
                    int start = filePathWithDot.indexOf(packageName)
                    println 'start: ' + start
                    if (start != -1) {
                        int end = filePath.length() - 6
                        String className = filePathWithDot.substring(start, end)
                        println 'className: ' + className
                        CtClass ctClass = pool.getCtClass(className)
                        if (ctClass.isFrozen()) {
                            ctClass.defrost()
                        }
                        if (ctClass.hasAnnotation(PrintLog.class)) {
                            for (CtMethod ctMethod : ctClass.getDeclaredMethods()) {
                                println 'method: ' + ctMethod
                                String code = """ android.util.Log.d("PrintLog","enter method");
                                                """
                                ctMethod.insertBefore(code)
                                code = """ android.util.Log.d("PrintLog","exit method");
                                                """
                                ctMethod.insertAfter(code)
                            }
                        }
                        ctClass.writeFile(directoryInput.file.absolutePath)
                        ctClass.detach()
                    }
                }
            }
        }
        //处理完输入文件之后，要把输出给下一个任务
        def dest = outputProvider.getContentLocation(directoryInput.name,
                directoryInput.contentTypes, directoryInput.scopes,
                Format.DIRECTORY)
        FileUtils.copyDirectory(directoryInput.file, dest)
    }

    /**
     * 处理Jar中的class文件
     */
    static void handleJarInputs(JarInput jarInput, TransformOutputProvider outputProvider) {
        if (jarInput.file.getAbsolutePath().endsWith(".jar")) {
            //重名名输出文件,因为可能同名,会覆盖
            def jarName = jarInput.name
            def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
            if (jarName.endsWith(".jar")) {
                jarName = jarName.substring(0, jarName.length() - 4)
            }
            JarFile jarFile = new JarFile(jarInput.file)
            Enumeration enumeration = jarFile.entries()
            File tmpFile = new File(jarInput.file.getParent() + File.separator + "classes_temp.jar")
            //避免上次的缓存被重复插入
            if (tmpFile.exists()) {
                tmpFile.delete()
            }
            JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tmpFile))
//            //用于保存
//            while (enumeration.hasMoreElements()) {
//                JarEntry jarEntry = (JarEntry) enumeration.nextElement()
//                String entryName = jarEntry.getName()
//                ZipEntry zipEntry = new ZipEntry(entryName)
//                InputStream inputStream = jarFile.getInputStream(jarEntry)
//                //插桩class
//                if (entryName.endsWith(".class") && !entryName.startsWith("R\$")
//                        && !"R.class".equals(entryName) && !"BuildConfig.class".equals(entryName)
//                        && "android/support/v4/app/FragmentActivity.class".equals(entryName)) {
//                    //class文件处理
//                    println '----------- deal with "jar" class file <' + entryName + '> -----------'
//                    jarOutputStream.putNextEntry(zipEntry)
//                    ClassReader classReader = new ClassReader(IOUtils.toByteArray(inputStream))
//                    ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
//                    ClassVisitor cv = new TestClassVisitor(classWriter)
//                    classReader.accept(cv, EXPAND_FRAMES)
//                    byte[] code = classWriter.toByteArray()
//                    jarOutputStream.write(code)
//                } else {
//                    jarOutputStream.putNextEntry(zipEntry)
//                    jarOutputStream.write(IOUtils.toByteArray(inputStream))
//                }
//                jarOutputStream.closeEntry()
//            }
            //结束
            jarOutputStream.close()
            jarFile.close()
            def dest = outputProvider.getContentLocation(jarName + md5Name,
                    jarInput.contentTypes, jarInput.scopes, Format.JAR)
            FileUtils.copyFile(tmpFile, dest)
            tmpFile.delete()
        }
    }
}
